package com.superbytecode.stencil.wrapper;

import cn.hutool.core.bean.BeanUtil;
import com.superbytecode.stencil.entities.TemplateContext;
import com.superbytecode.stencil.enums.TemplateEnum;
import com.superbytecode.stencil.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * All rights Reserved, Designed By www.super-master.com
 *
 * @ProjectName: code-stencil
 * @Package: com.superbytecode.stencil.service
 * @ClassName: TemplateEngineService
 * @Description: []
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2024/10/14 14:19
 * @Version: V1.0
 * @Copyright: 2024 www.super-master.cn Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@Slf4j
public class CodeEngineSelector {
    final ZipOutputStream zip;

    public static CodeEngineSelector builder(ZipOutputStream zip) {
        return new CodeEngineSelector(zip);
    }

    public void createForVelocity(TemplateContext context) {
        //设置velocity资源加载器
        initializer();
        // 封装模板数据 property-->map
        Map<String, Object> contextMap = BeanUtil.beanToMap(context);
        // 获取模板列表
        VelocityContext velocity = new VelocityContext(contextMap);
        // 渲染模板
        this.loadLocalTemplates(context.getJavaBeanName()).forEach(templateMap -> {
            String templateName = MapUtils.getString(templateMap, "templateFilePath");
            String fileName = MapUtils.getString(templateMap, "codeFilePath");
            this.rendering(velocity, templateName, fileName);
        });
    }

    private void rendering(VelocityContext context, String templateName, String fileName) {
        //渲染模板
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate(templateName, "UTF-8");
        tpl.merge(context, sw);
        //
        try {
            zip.putNextEntry(new ZipEntry(fileName));
            IOUtils.write(sw.toString(), zip, "UTF-8");
            IOUtils.closeQuietly(sw);
            zip.closeEntry();
        } catch (Exception e) {
            throw new RuntimeException("rendering error: ", e);
        }
    }

    private void initializer() {
        // 引擎的配置属性
        Properties props = new Properties();
        // props.setProperty("resource.loader.file.path", "/path/to/templates");
        // 设置类路径加载器的实现类
        props.put("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        // 定义字符集
        props.put("resource.default_encoding", "org.apache.velocity.runtime.log.NullLogChute");
        // 设置模板文件的输入编码和输出编码，这里都设置为UTF-8
        props.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
        Velocity.init(props);
    }

    /**
     * 加载本地模板信息
     *
     * @return 模板名称
     */
    private List<Map<String, String>> loadLocalTemplates(String name) {
        List<Map<String, String>> templates = new ArrayList<>();
        // java地址：main/java
        String javaPath = StringUtil.concatPath("main", "java");
        // java/resources/mapper
        String mybatisPath = StringUtil.concatPath("main", "resources", "mapper");
        String mybatisPlusPath = StringUtil.concatPath("main", "resources", "plus-mapper");
        TemplateEnum.load().stream().filter(t -> Objects.nonNull(t.getCode())).forEach(template -> {
            Map<String, String> objectHashMap = new HashMap<>();
            String suffix = template.getSuffix();
            String moduleName = template.getModule();
            String fileName = template.getFile();
            String templateFilePath = this.makeupTemplate(fileName, suffix);
            String className = this.makeupClassName(name, template);
            String codeFilePath = "";
            switch (suffix) {
                case ".java":
                    codeFilePath = StringUtil.concatPath(javaPath, className);
                    break;
                case ".xml":
                    codeFilePath = StringUtil.concatPath(mybatisPath, className);
                    break;
                case ".sql":
                    break;
                case ".vue":
                    break;
            }
            objectHashMap.put("templateFilePath", templateFilePath);
            objectHashMap.put("codeFilePath", codeFilePath);
            templates.add(objectHashMap);
        });
        return templates;
    }

    private String makeupClassName(String name, TemplateEnum template) {
        String className = StringUtil.capitalizeFirstLetter(name);
        if (Objects.equals("MybatisPlusMapper", template.getFile())) {
            className = className.concat("Plus");
        }
        return template.isDomain(template.getCode())//
                ? className.concat(template.getSuffix())//
                : className.concat(template.getModule()).concat(template.getSuffix());
    }

    private String makeupTemplate(String module, String suffix) {
        return StringUtil.concatPath("velocity", module + suffix + ".vm");
    }

    private CodeEngineSelector(ZipOutputStream zip) {
        this.zip = zip;
    }
}